package cn.iocoder.yudao.module.infra.service.file;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONObject;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
import cn.iocoder.yudao.module.infra.framework.file.core.client.chevereto.CheveretoFileClient;
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;
import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileMediaUtils;
import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.CompletableFuture;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;

/**
 * 文件 Service 实现类
 *
 * @author 芋道源码
 */
@Service
public class FileServiceImpl implements FileService {

    private static final Logger log = LoggerFactory.getLogger(FileServiceImpl.class);

    @Resource
    private FileConfigService fileConfigService;

    @Resource
    private FileMapper fileMapper;

    /**
     * 获取 Chevereto 客户端，如不存在则返回 null
     */
    private CheveretoFileClient getCheveretoClient() {
        // 获取所有文件配置
        List<FileConfigDO> configs = fileConfigService.getFileConfigList();
        for (FileConfigDO config : configs) {
            if (config.getStorage().equals(FileStorageEnum.CHEVERETO.getStorage())) {
                FileClient client = fileConfigService.getFileClient(config.getId());
                if (client instanceof CheveretoFileClient) {
                    return (CheveretoFileClient) client;
                }
            }
        }
        return null;
    }

    @Override
    public PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO) {
        return fileMapper.selectPage(pageReqVO);
    }

    @Override
    @SneakyThrows
    public String createFile(String name, String path, byte[] content) {
        return createFile(name, path, content, true);
    }

    @SneakyThrows
    public String createFile(String name, String path, byte[] content, boolean autoCompress) {
        // 计算默认的 path 名
        String type = FileTypeUtils.getMineType(content, name);
        if (StrUtil.isEmpty(path)) {
            path = FileUtils.generatePath(content, name);
        }
        // 如果 name 为空，则使用 path 填充
        if (StrUtil.isEmpty(name)) {
            name = path;
        }

        // 根据文件类型智能选择存储客户端
        FileClient client = null;

        if (FileMediaUtils.isCheveretoSupported(name)) {
            // 图片和视频使用 Chevereto
            CheveretoFileClient cheveretoClient = getCheveretoClient();
            if (cheveretoClient != null) {
                client = cheveretoClient;
            } else {
                log.warn("[createFile][文件:{} 适合使用 Chevereto，但未找到 Chevereto 配置]", name);
            }
        }

        if (client == null){
            // 非图片视频文件使用默认客户端
            client = fileConfigService.getMasterFileClient();
        }
        
        Assert.notNull(client, "文件客户端不能为空");

        String url;
        // 检查 MD5，避免重复上传
        String md5 = DigestUtil.md5Hex(content);
        if (client instanceof CheveretoFileClient cheveretoClient) {
            String existingUrl = cheveretoClient.getFileUrlByMD5(md5);
            if (StringUtils.isNotEmpty(existingUrl)) {
                log.info("[createFile][文件:{} MD5:{} 已存在，直接返回URL]", name, md5);
                return existingUrl;
            }
            url = client.upload(content, path, type);
        } else {
            // 上传文件
            url = client.upload(content, path, type);
            // 保存到数据库
            FileDO file = new FileDO();
            file.setConfigId(client.getId());
            file.setName(name);
            file.setPath(path);
            file.setUrl(url);
            file.setType(type);
            file.setSize(content.length);
            file.setMd5(md5);
            fileMapper.insert(file);
        }

        
        return url;
    }

    @Override
    public Long createFile(FileCreateReqVO createReqVO) {
        FileDO file = BeanUtils.toBean(createReqVO, FileDO.class);
        fileMapper.insert(file);
        return file.getId();
    }

    @Override
    public void deleteFile(Long id) throws Exception {
        // 校验存在
        FileDO file = validateFileExists(id);

        // 从文件存储器中删除
        FileClient client = fileConfigService.getFileClient(file.getConfigId());
        Assert.notNull(client, "客户端({}) 不能为空", file.getConfigId());
        client.delete(file.getPath());

        // 删除记录
        fileMapper.deleteById(id);
    }

    private FileDO validateFileExists(Long id) {
        FileDO fileDO = fileMapper.selectById(id);
        if (fileDO == null) {
            throw exception(FILE_NOT_EXISTS);
        }
        return fileDO;
    }

    @Override
    public byte[] getFileContent(Long configId, String path) throws Exception {
        FileClient client = fileConfigService.getFileClient(configId);
        Assert.notNull(client, "客户端({}) 不能为空", configId);
        return client.getContent(path);
    }

    @Override
    public FilePresignedUrlRespVO getFilePresignedUrl(String path) throws Exception {
        FileClient fileClient = fileConfigService.getMasterFileClient();
        FilePresignedUrlRespDTO presignedObjectUrl = fileClient.getPresignedObjectUrl(path);
        return BeanUtils.toBean(presignedObjectUrl, FilePresignedUrlRespVO.class,
                object -> object.setConfigId(fileClient.getId()));
    }

    @Override
    public String getFileUrlByMD5(String md5) {
        if (StringUtils.isEmpty(md5)) {
            return null;
        }
        
        FileDO file = fileMapper.selectOne(FileDO::getMd5, md5);
        return file != null ? file.getUrl() : null;
    }
    
    /**
     * 获取图片信息 (仅适用于 Chevereto)
     * 
     * @param shortCode 图片短码
     * @return 图片信息
     */
    @Override
    public JSONObject getImageInfo(Long configId, String shortCode) {
        FileClient client = fileConfigService.getFileClient(configId);
        if (client instanceof CheveretoFileClient cheveretoClient) {
            return cheveretoClient.getImageInfo(shortCode);
        }
        throw exception(new ErrorCode(999999999, "不支持的客户端类型，无法获取图片信息"));
    }
    
    /**
     * 异步上传图片
     * 
     * @param name 文件名
     * @param path 路径
     * @param content 文件内容
     * @param autoCompress 是否自动压缩
     * @return CompletableFuture
     */
    @Override
    public CompletableFuture<String> asyncCreateFile(String name, String path, byte[] content, boolean autoCompress) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return createFile(name, path, content, autoCompress);
            } catch (Exception e) {
                throw new RuntimeException("异步上传文件失败", e);
            }
        });
    }

    public CompletableFuture<String> asyncCreateFile(String name, String path, byte[] content) {
        return asyncCreateFile(name, path, content, true);
    }

}
